class RuleSystem extends System {
    constructor(world) {
        super(world);
        this._gameOver = false;
        this._spawnTimer = 0;
        this._targets = [];
        this.getWorld().getMessageManager().subscribe('COLLISION', (message) => this.onCollision(message));
        this.start();
    }

    start() {
        this.createPlayerEntity();
        this.spawn(config.initialSpawnCount);
    }

    createPlayerEntity() {
        const player = this.getWorld().createEntity();
        player.addComponent(new BallComponent(Colors.White));
        player.addComponent(new PositionComponent(config.width / 2, config.height / 2));
        player.addComponent(new CircleCollisionComponent(config.ballRadius));
        player.addComponent(new PlayerControllerComponent(0.25));
    }

    onCollision(message) {
        this._targets.push(message.target);
    }

    getPlayerEntity() {
        for (const entity of this.getWorld().getEntities()) {
            if (entity.getComponent(PlayerControllerComponent)) {
                return entity;
            }
        }
    }

    execute() {
        if (!this._gameOver) {
            this.handleCollisionTargets();
            this.checkSpawnTimer();
        }
    }

    handleCollisionTargets() {
        const player = this.getPlayerEntity();
        const playerBall = player.getComponent(BallComponent);

        for (const target of this._targets) {
            const {color} = target.getComponent(BallComponent);
            playerBall.color = this.mixColor(playerBall.color, color);
            if (playerBall.color === Colors.Black) {
                console.log('Game over');
                this._gameOver = true;
                this.getWorld().getMessageManager().notify({type: 'GAME_OVER'});
            }
            else if (playerBall.color === Colors.White) {
                target.addComponent(new ScoreComponent(color));
            }

            target.removeComponent(BallComponent);
            target.removeComponent(CircleCollisionComponent);
        }

        this._targets = [];
    }

    checkSpawnTimer() {
        this._spawnTimer += time.delta;
        if (this._spawnTimer >= config.spawnSpan) {
            this._spawnTimer = 0;
            this.spawn(config.spawnCount);
        }
    }

    spawn(count) {
        console.log('spawn', count);
        const {width, height, ballRadius: radius} = config;
        const world = this.getWorld();
        for (let i = 0; i < count; i++) {
            let x = 0, y = 0, color = Colors.Red;
            const entity = world.createEntity();
            const positionComponent = new PositionComponent(x, y);
            entity.addComponent(positionComponent);
            entity.addComponent(new CircleCollisionComponent(radius));

            // // 20% each for red, green  and blue, others 10% each
            // const colorRandomResult = Math.random() * 10;
            // if (colorRandomResult >= 8) {
            //     color = Colors.Red;
            // }
            // else if (colorRandomResult >= 6) {
            //     color = Colors.Green;
            // }
            // else if (colorRandomResult >= 4) {
            //     color = Colors.Blue;
            // }
            // else if (colorRandomResult >= 3) {
            //     color = Colors.Orange
            // }
            // else if (colorRandomResult >= 2) {
            //     color = Colors.Cyan;
            // }
            // else if (colorRandomResult >= 1) {
            //     color = Colors.Purple;
            // }
            // else {
            //     color = Colors.Black;
            // }

            // 2/9 each for red, green  and blue, others 1/9 each
            const colorRandomResult = Math.random() * 9;
            if (colorRandomResult >= 7) {
                color = Colors.Red;
            }
            else if (colorRandomResult >= 5) {
                color = Colors.Green;
            }
            else if (colorRandomResult >= 3) {
                color = Colors.Blue;
            }
            else if (colorRandomResult >= 2) {
                color = Colors.Orange
            }
            else if (colorRandomResult >= 1) {
                color = Colors.Cyan;
            }
            else {
                color = Colors.Purple;
            }

            entity.addComponent(new BallComponent(color));

            do {
                if (Math.random() > 0.5) {
                    // Spawn to the left or right of the screen
                    x = Math.random() > 0.5 ? 0 - radius : width + radius;
                    y = Math.random() * height;
                }
                else {
                    // Spawn to the top or bottom of the screen
                    x = Math.random() * width;
                    y = Math.random() > 0.5 ? 0 - radius : height + radius;
                }

                positionComponent.x = x;
                positionComponent.y = y;
            } while (this.hasCollision(entity))
        }
    }

    hasCollision(entity) {
        for (const target of this.getWorld().getEntities()) {
            if (target !== entity && !target.getComponent(PlayerControllerComponent) && CollisionSystem.collide(entity, target)) {
                return true;
            }
        }

        return false;
    }

    mixColor(color1, color2) {
        switch (color1) {
            case Colors.White:
                return color2;

            case Colors.Red: {
                switch (color2) {
                    case Colors.Red:
                        return Colors.Black;

                    case Colors.Green:
                        return Colors.Orange;

                    case Colors.Blue:
                        return Colors.Purple;

                    case Colors.Orange:
                        return Colors.Black;

                    case Colors.Cyan:
                        return Colors.White;

                    case Colors.Purple:
                        return Colors.Black;
                }
                break;
            }

            case Colors.Green: {
                switch (color2) {
                    case Colors.Red:
                        return Colors.Orange;

                    case Colors.Green:
                        return Colors.Black;

                    case Colors.Blue:
                        return Colors.Cyan;

                    case Colors.Orange:
                        return Colors.Black;

                    case Colors.Cyan:
                        return Colors.Black;

                    case Colors.Purple:
                        return Colors.White;
                }
                break;
            }

            case Colors.Blue: {
                switch (color2) {
                    case Colors.Red:
                        return Colors.Purple;

                    case Colors.Green:
                        return Colors.Cyan;

                    case Colors.Blue:
                        return Colors.Black;

                    case Colors.Orange:
                        return Colors.White;

                    case Colors.Cyan:
                        return Colors.Black;

                    case Colors.Purple:
                        return Colors.Black;
                }
                break;
            }

            case Colors.Orange: {
                switch (color2) {
                    case Colors.Red:
                        return Colors.Black;

                    case Colors.Green:
                        return Colors.Black;

                    case Colors.Blue:
                        return Colors.White;

                    case Colors.Orange:
                        return Colors.Black;

                    case Colors.Cyan:
                        return Colors.Black;

                    case Colors.Purple:
                        return Colors.Black;
                }
                break;
            }

            case Colors.Cyan: {
                switch (color2) {
                    case Colors.Red:
                        return Colors.White;

                    case Colors.Green:
                        return Colors.Black;

                    case Colors.Blue:
                        return Colors.Black;

                    case Colors.Orange:
                        return Colors.Black;

                    case Colors.Cyan:
                        return Colors.Black;

                    case Colors.Purple:
                        return Colors.Black;
                }
                break;
            }

            case Colors.Purple: {
                switch (color2) {
                    case Colors.Red:
                        return Colors.Black;

                    case Colors.Green:
                        return Colors.White;

                    case Colors.Blue:
                        return Colors.Black;

                    case Colors.Orange:
                        return Colors.Black;

                    case Colors.Cyan:
                        return Colors.Black;

                    case Colors.Purple:
                        return Colors.Black;
                }
                break;
            }

            case Colors.Black:
            default:
                return Colors.Black;
        }

        // switch (color1) {
        //     case Colors.White:
        //         return color2;
        //
        //     case Colors.Red: {
        //         switch (color2) {
        //             case Colors.Red:
        //                 return Colors.White;
        //
        //             case Colors.Green:
        //                 return Colors.Orange;
        //
        //             case Colors.Blue:
        //                 return Colors.Purple;
        //
        //             case Colors.Orange:
        //                 return Colors.Green;
        //
        //             case Colors.Cyan:
        //                 return Colors.Black;
        //
        //             case Colors.Purple:
        //                 return Colors.Blue;
        //
        //             case Colors.Black:
        //                 return Colors.Cyan;
        //         }
        //         break;
        //     }
        //
        //     case Colors.Green: {
        //         switch (color2) {
        //             case Colors.Red:
        //                 return Colors.Orange;
        //
        //             case Colors.Green:
        //                 return Colors.White;
        //
        //             case Colors.Blue:
        //                 return Colors.Cyan;
        //
        //             case Colors.Orange:
        //                 return Colors.Red;
        //
        //             case Colors.Cyan:
        //                 return Colors.Blue;
        //
        //             case Colors.Purple:
        //                 return Colors.Black;
        //
        //             case Colors.Black:
        //                 return Colors.Purple;
        //         }
        //         break;
        //     }
        //
        //     case Colors.Blue: {
        //         switch (color2) {
        //             case Colors.Red:
        //                 return Colors.Purple;
        //
        //             case Colors.Green:
        //                 return Colors.Cyan;
        //
        //             case Colors.Blue:
        //                 return Colors.White;
        //
        //             case Colors.Orange:
        //                 return Colors.Black;
        //
        //             case Colors.Cyan:
        //                 return Colors.Green;
        //
        //             case Colors.Purple:
        //                 return Colors.Red;
        //
        //             case Colors.Black:
        //                 return Colors.Orange;
        //         }
        //         break;
        //     }
        //
        //     case Colors.Orange: {
        //         switch (color2) {
        //             case Colors.Red:
        //                 return Colors.Green;
        //
        //             case Colors.Green:
        //                 return Colors.Red;
        //
        //             case Colors.Blue:
        //                 return Colors.Black;
        //
        //             case Colors.Orange:
        //                 return Colors.White;
        //
        //             case Colors.Cyan:
        //                 return Colors.Purple;
        //
        //             case Colors.Purple:
        //                 return Colors.Cyan;
        //
        //             case Colors.Black:
        //                 return Colors.Blue;
        //         }
        //         break;
        //     }
        //
        //     case Colors.Cyan: {
        //         switch (color2) {
        //             case Colors.Red:
        //                 return Colors.Black;
        //
        //             case Colors.Green:
        //                 return Colors.Blue;
        //
        //             case Colors.Blue:
        //                 return Colors.Green;
        //
        //             case Colors.Orange:
        //                 return Colors.Purple;
        //
        //             case Colors.Cyan:
        //                 return Colors.White;
        //
        //             case Colors.Purple:
        //                 return Colors.Orange;
        //
        //             case Colors.Black:
        //                 return Colors.Red;
        //         }
        //         break;
        //     }
        //
        //     case Colors.Purple: {
        //         switch (color2) {
        //             case Colors.Red:
        //                 return Colors.Blue;
        //
        //             case Colors.Green:
        //                 return Colors.Black;
        //
        //             case Colors.Blue:
        //                 return Colors.Red;
        //
        //             case Colors.Orange:
        //                 return Colors.Cyan;
        //
        //             case Colors.Cyan:
        //                 return Colors.Orange;
        //
        //             case Colors.Purple:
        //                 return Colors.White;
        //
        //             case Colors.Black:
        //                 return Colors.Green;
        //         }
        //         break;
        //     }
        //
        //     case Colors.Black:
        //     default:
        //         return Colors.Black;
        // }
    }
}